﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;

namespace SimpleIR
{
	public abstract class IRByteCode
	{
		/// <summary>
		/// The opcode for this
		/// </summary>
		public readonly OpCode.Op OperationCode;

		/// <summary>
		/// The information on the source location that this bytecode corresponds to
		/// </summary>
		public readonly IRSourceLocation SourceLocationInfo;

		/// <summary>
		/// The unique use/mod location number assigned to this bytecode
		/// </summary>
		private int m_usemodnum;

		////////////////////////
		//Other methods
		internal IRByteCode(OpCode.Op opc, IRSourceLocation sloc)
		{
			this.OperationCode = opc;
			this.SourceLocationInfo = sloc;
			this.m_usemodnum = 0;
		}
		
		/// <summary>
		/// Return the use/mod location that this bytecode corresponds to.
		/// </summary>
		public int UseModLoc 
		{ get { return this.m_usemodnum; } }

		/// <summary>
		/// Return true if this bytecode ends a basic block.
		/// </summary>
		public bool IsStmtValidBlockEnd
		{ get { return (this is IRBranchOp); } }

		/////////////////////////
		//Support for outputting and displaying the stmts for compilation and further inspection

		/// <summary>
		/// Set the use mod location
		/// </summary>
		internal void SetUseModLoc(CtrGen cgn)
		{ this.m_usemodnum = cgn.GenNextVal(); }

		/// <summary>
		/// Generate a statement number if possible, return empty string if not yet given one
		/// </summary>
		internal String GenStmtNumber()
		{ return "L" + this.m_usemodnum.ToString(); }

		/// <summary>
		/// Get all the targets for branches at the end of a BB and put them in tl.
		/// </summary>
		public virtual void GetBranchTargets(List<uint> tl)
		{ return; }
	}

	/// <summary>
	/// Contains all the info on where the opcode or method is defined in both the assembly and in the source file (if possible).
	/// </summary>
	public sealed class IRSourceLocation : IEquatable<IRSourceLocation>, IComparable<IRSourceLocation>
	{
		public readonly string FileName;
		public readonly int Line;
		public readonly int CCIOffset;
		
		public IRSourceLocation(string sourcefilename, int line, int ccioffset)
		{
			this.FileName = sourcefilename;
			this.Line = line;
			this.CCIOffset = ccioffset;
		}

		public override string ToString()
		{ return System.IO.Path.GetFileName(this.FileName) + " line: " + this.Line.ToString(); }

		public bool IsDummyLocation
		{ get { return this.Line == -1; } }

		public override int GetHashCode()
		{ return this.Line ^ this.FileName.GetHashCode() ^ this.CCIOffset; }

		public override bool Equals(object obj)
		{ return (obj is IRSourceLocation) && this.Equals((IRSourceLocation)obj); }

		public bool Equals(IRSourceLocation other)
		{ return (this.Line == other.Line) && this.FileName.Equals(other.FileName) && (this.CCIOffset == other.CCIOffset) ; }

		public int CompareTo(IRSourceLocation other)
		{
			int fdif = this.FileName.CompareTo(other.FileName);
			if(fdif != 0)
				return fdif;
			else
				return (this.Line != other.Line) ? (this.Line - other.Line) : (this.CCIOffset - other.CCIOffset); 
		}
	}
}
